/****************************************************************************\
 * @file     BR_Error.c
 * @author   傅帅
 * @date     2019/12/27
 * @brief    错误管理任务
 *
 ******************************************************************************
 * @attent   将32种错误分为4组，每组触发同样的处理
 ****************************************************************************/
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "BR_Common.h"

/* 全局变量 */
TaskHandle_t BR_ErrorManagerTaskHandler;
BR_Error_t BR_RaisedErrorList;              // 已抛出错误的链表头
QueueHandle_t BR_ErrorQueue;                // 抛出错误时通过队列通知错误管理任务
/* 私有变量 */
uint32_t prvErrorBits = 0xFFFFFFFF;         // 已发生的错误记录,0为有错误，1为无错误(Flash默认为全1)
static BR_Error_t prvErrorTbl[32];
static uint8_t prvGroupNum[4] = {0, 0, 0, 0};  // 四个组已有的Error数
static void prvErrorListInsert(BR_Error_t *ErrorToInsert);

// 每个组的错误处理,由用户在此处实现
void BR_vErrorHandlerGroup0(void){}
void BR_vErrorHandlerGroup1(void){}
void BR_vErrorHandlerGroup2(void){}
void BR_vErrorHandlerGroup3(void){}
    
/* -------------------------------------------------- Public ----------------------------------------------- */
/** @brief 错误管理任务本体 **/
void BR_vErrorManagerTask(void)
{
    // 等待任务通知，有错误抛出时更新Flash，调用对应组函数
    while(1)
    {
        uint32_t Bit;
        xQueueReceive(BR_ErrorQueue, &Bit, portMAX_DELAY);
        prvErrorBits &= ~Bit;  // 把传入的位置0
        taskENTER_CRITICAL();
        STMFLASH_Write(BR_ERROR_FLASH_BASE, &prvErrorBits, 1);     // 更新Flash
        taskEXIT_CRITICAL();
        if(Bit & BR_ERROR_GROUP_0_BITS) BR_vErrorHandlerGroup0();
        else if(Bit & BR_ERROR_GROUP_1_BITS) BR_vErrorHandlerGroup1();
        else if(Bit & BR_ERROR_GROUP_2_BITS) BR_vErrorHandlerGroup2();
        else if(Bit & BR_ERROR_GROUP_3_BITS) BR_vErrorHandlerGroup3();
    }
}

/** @brief 错误管理初始化 **/
void BR_vErrorManagerInit(void)
{
    uint8_t Index;
    BR_RaisedErrorList.pNext = NULL;
    BR_ErrorQueue = xQueueCreate(5, 4);                     // Len=5 Size=4Byte
    STMFLASH_Read(BR_ERROR_FLASH_BASE, &prvErrorBits, 1);   // 读取之前的错误状态
    /* 将上次发生的错误送入已抛出错误链表 */
    for(Index = 0; Index < 32; Index++)
    {
        if(( prvErrorBits & ( 1 << Index )) == 0)           // 若某位prvErrorBits为0,将对应结构体插入链表
            prvErrorListInsert(&prvErrorTbl[Index]);
    }
}

/**
 * @brief  根据组别创建一个错误
    @args   ErrorName   : 错误名称
            ErrorGroup  : 错误分组 0-3
    @retval 成功返回错误句柄, 失败返回NULL
 */
BR_ErrorHandler_t BR_xErrorCreate(char *ErrorName, uint8_t ErrorGroup)
{
    if(prvGroupNum[ErrorGroup] == 8) return NULL;                  // 若已满,返回零
    uint32_t Bit = 1 << 8 * ErrorGroup << prvGroupNum[ErrorGroup]; // 计算创建的Error对应的Bit
    uint8_t Index = 8 * ErrorGroup + prvGroupNum[ErrorGroup];      // 计算创建的Error对应的Index
    prvErrorTbl[Index].ErrorBit = Bit;
    prvErrorTbl[Index].ErrorIndex = Index;
    prvErrorTbl[Index].ErrorName = ErrorName;
    prvGroupNum[ErrorGroup]++;
    return &prvErrorTbl[Index];
}

/** @brief 错误抛出 **/
void BR_vErrorRaise(BR_ErrorHandler_t Error)
{
    if(( prvErrorBits & Error->ErrorBit ) != 0)    // 若错误不在已抛出错误中，插入链表
        prvErrorListInsert(&prvErrorTbl[Error->ErrorIndex]);
    xQueueSend(BR_ErrorQueue, &Error->ErrorBit, 0);
}

/** @brief 中断中抛出错误 **/
void BR_vErrorRaiseFromISR(BR_ErrorHandler_t Error, BaseType_t* pxHigherPriorityTaskWoken)
{
    if(( prvErrorBits & Error->ErrorBit ) != 0)    // 若错误不在已抛出错误中，插入链表
        prvErrorListInsert(&prvErrorTbl[Error->ErrorIndex]);
    xQueueSendFromISR(BR_ErrorQueue, &Error->ErrorBit, pxHigherPriorityTaskWoken);
}

/** @brief 获取错误
    @return 32位分别对应4个组每个组8个错误，0为有错，1为无错 **/
uint32_t BR_iGetErrorBits(void)
{
    return prvErrorBits;
}

/** @brief 清空错误 **/
void BR_vClearErrorBits(void)
{
    prvErrorBits = 0xFFFFFFFF;
    BR_RaisedErrorList.pNext = NULL;
    STMFLASH_Write(BR_ERROR_FLASH_BASE, &prvErrorBits, 1);  // 清零Flash
}

/* -------------------------------------------------- Private ----------------------------------------------- */
static void prvErrorListInsert(BR_Error_t *ErrorToInsert)
{
    if(BR_RaisedErrorList.pNext == NULL) {  // 链表为空时第一次插入
        BR_RaisedErrorList.pNext = ErrorToInsert;
        BR_RaisedErrorList.pNext->pNext = NULL;
    }else{                                  // 正常插入
        ErrorToInsert->pNext = BR_RaisedErrorList.pNext;
        BR_RaisedErrorList.pNext = ErrorToInsert;
    }
}
